////////////////////////////////////////////////////////////////////////////////
// File:        TNodBase.cpp
// Purpose:     Implements TNodBase class for the basics license operations
// Author:      Tukero
// Modified by: 
// Copyright:   (c) Tukero <tukero@gmail.com>
// Licence:     GNU/LGPL v3
////////////////////////////////////////////////////////////////////////////////

#include "TNodBase.h"
#include "CurrentAppHeaders.h"
#include "Utils.h"
#include "wx/httpbuilder.h"
#include "LicenseChecking.h"
#include "Settings.h"
#include "UpdatesChecker.h"
#include <wx/txtstrm.h>
#include <wx/zstream.h>
#include <wx/uri.h>

WX_DEFINE_LIST(LinkListThreads);

//
// Removes the thread from the list
//
void TNodBase::RemoveChildThread(wxThread *licenseChecking)
{
    //wxCriticalSectionLocker locker(wxGetApp().m_MutexThreadsList);
    m_ListOfLicenseThreads.DeleteObject(licenseChecking);
}

//
// Removes all threads in the link list
//
void TNodBase::DeleteChildThreads()
{
    LinkListThreads::iterator iter;
    //wxCriticalSectionLocker locker(wxGetApp().m_MutexThreadsList);

    for (iter = m_ListOfLicenseThreads.begin(); m_ListOfLicenseThreads.GetCount();iter = m_ListOfLicenseThreads.begin())
    {        
        wxThread *thread = *iter;
        thread->Delete();
        m_ListOfLicenseThreads.DeleteObject(thread);
    }
    m_ListOfLicenseThreads.Clear();
}

//
// Loads the eset information and loads the server to check the TNod data (new versions, new license servers, etc)
//
TNodBase::TNodBase()
{                                        
    wxGetApp().m_NewVersionFound = false;

    m_MsgWithNewVersionOnly = false;
    m_ProcessNewVersionMsg = true;

    m_DownloadEvent = NULL;
    m_HandlerForEvent = NULL;
    m_EventType = 0;
    
    m_Actions2Do = 0;
    m_IsMain = false;

    m_EsetInfo = new EsetInfo();
    m_NewVersionMsg = new NewVersionMsg();

    m_ListOfLicenseThreads.SetKeyType(wxKEY_INTEGER);
    m_ValidLicenses.SetKeyType(wxKEY_STRING);
    m_CheckedLicenses.SetKeyType(wxKEY_STRING);


    //m_CheckedLicenses.Clear();





    /*
    wxListKey strKey(wxT("asdf"));

    
    m_CheckedLicenses.Append(strKey.GetString(),new License(wxT("u"),wxT("i")));
    if ( m_CheckedLicenses.Find(strKey) != NULL )
        wxMessageBox(wxT("found"));
    else
        wxMessageBox(wxT("not found!"));

    //m_CheckedLicenses.Append(wxT("asdfg"),new License(wxT("u"),wxT("i"));
    //m_CheckedLicenses.Append(wxT("asdfgh"),new License(wxT("u"),wxT("i"));
    */
}

void TNodBase::CheckUpdates()
{
    UpdatesChecker* newUpdatesChecker = new UpdatesChecker( UpdatesChecker::CHECK_ALL, m_NewVersionMsg, m_EsetInfo, wxTHREAD_JOINABLE );
    newUpdatesChecker->SetNewVersionIndicator( &wxGetApp().m_NewVersionFound );
    if ( newUpdatesChecker->Create() == wxTHREAD_NO_ERROR )
    {
        newUpdatesChecker->Run();
        newUpdatesChecker->Wait();
    }
    delete newUpdatesChecker;
}

//
// Updates TNod Data, inserts a new license and warning if a new version appears
//
void TNodBase::Start()
{
    
    //NewVersionMsg *newVersionMsg = new NewVersionMsg();
    
    /*if ( m_Actions2Do == INSERT_LICENSE && m_License2Insert != NULL)
    {
        InsertLicenseNow(*m_License2Insert);
        return;
    }*/
    
    wxGetApp().ChangeMsgBalloon(1,GETLANG(69),true); //connecting message
    CheckUpdates();
    //UpdateTNodData(m_Actions2Do, m_NewVersionMsg);
    
    int totalValidServers = 0;

    //download licenses
    FindLicenses(&m_ValidLicenses,NULL, &totalValidServers);
    
    if ( (m_EventType & EVENT_GOTALLLICENSES) == EVENT_GOTALLLICENSES && m_HandlerForEvent && m_DownloadEvent )
    {
        wxCommandEvent newEvent(*m_DownloadEvent);
        m_DownloadEvent = NULL;
        m_HandlerForEvent = NULL;

        newEvent.SetInt(2);
        wxPostEvent( m_HandlerForEvent, newEvent );            
    }

    ProcessNewVersionMsg(m_NewVersionMsg);    
}


void TNodBase::SetEventReceiver(wxEvtHandler* handlerForEvent, wxCommandEvent* event2Send, int eventType)
{
    if (handlerForEvent != NULL && event2Send != NULL && eventType > 0 )
    {
        m_DownloadEvent = event2Send;
        m_HandlerForEvent = handlerForEvent;
        m_EventType = eventType;
    }
}

//
// Creates threads for license checking 
//
void TNodBase::CreateLicenseThreads(LinkListLicense *licenses4Checking, unsigned int actions, LinkListLicense *validLicenses, License *excludedLicense, int maxValidLicenses, bool licWithNoExpirationOnly)
{
    if ( licenses4Checking == NULL || actions == 0 ) 
        return;

    bool isTrialIncluded = SETTINGS->IncludeTrial();
    
    bool isTrialLicense = false; //will be TRUE if the username begins with TRIAL-XXXXX
    
    int lic2CheckInParallel = SETTINGS->GetLic2CheckInParallel();

    LinkListLicense::iterator iterLicense = licenses4Checking->begin();
    License *license=NULL;
    
    //m_CheckedLicenses.Clear();
    m_ListOfLicenseThreads.Clear();

    
    while (iterLicense != licenses4Checking->end() && !TestDestroy())
    //for (iterLicense = licenses4Checking->begin(); iterLicense != licenses4Checking->end() && !TestDestroy(); ++iterLicense)
    {
        license = *iterLicense;

        int num2Check = 0;
        //*wxGetApp().m_checkedLicenses = 0;

        wxString usernames2Show;// = wxT("\n");

        while (num2Check < lic2CheckInParallel && iterLicense != licenses4Checking->end() && !TestDestroy())
        {
            license = *iterLicense;

            isTrialLicense = (license->GetUsername().Matches(wxT("TRIAL-*")));
            bool checkLicense = false;
            
            if ( !isTrialLicense || (isTrialLicense && isTrialIncluded == true) )
            {
                checkLicense = true;

                if ( excludedLicense != NULL && excludedLicense->IsSameAs(*license) )
                    checkLicense = false;
            }
            
            if ( checkLicense == true )
            {
                usernames2Show += wxT("\n") + license->GetUsername();

                //creates a new thread and appends it to the list
                LicenseChecking *newChecking = NULL;

                newChecking = new LicenseChecking(num2Check,license,validLicenses,this,m_EsetInfo,actions);
                newChecking->m_LicWithNoExpirationOnly = licWithNoExpirationOnly;                

                if ( (m_EventType & EVENT_GOTLICENSE) == EVENT_GOTLICENSE )
                {
                    newChecking->m_DownloadEvent = m_DownloadEvent;
                    newChecking->m_HandlerForEvent = m_HandlerForEvent;                    
                }
                newChecking->m_checkedLicenses = &m_CheckedLicenses;

                m_ListOfLicenseThreads.Append(num2Check,newChecking);

                //run the thread
                if ( newChecking->Create() == wxTHREAD_NO_ERROR )
                {
                    newChecking->Run();

                    if ( license != NULL )
                        num2Check++;
                }
            }
            if (num2Check < lic2CheckInParallel)
                iterLicense++;
        }
        
        if ( num2Check > 0 )
        {
            if ( actions == LicenseChecking::CHECK_EXPIRATIONDATE )
                wxGetApp().ChangeMsgBalloon(1,GETLANG(68) + usernames2Show,true);//checking expiration date of. message
            else
                wxGetApp().ChangeMsgBalloon(1,GETLANG(66) + usernames2Show,true);//testing licenses. message

            int maxTimes = 30;
            int countTimes = 0;
            while (!TestDestroy() && countTimes < maxTimes)
            {
                tkSleep(500);
                countTimes++;

                if ( maxValidLicenses > 0 )
                {
                    if ( validLicenses != NULL && validLicenses->GetCount() >= (unsigned int)maxValidLicenses )
                        break;
                }
                if ( m_CheckedLicenses.GetCount() >= (unsigned int)num2Check )
                    break;
            }
        }
        
        DeleteChildThreads();
                
        if ( maxValidLicenses > 0 && validLicenses != NULL && validLicenses->GetCount() >= (unsigned int)maxValidLicenses )
            break;

        //gets the next license in the list
        
        if ( iterLicense != licenses4Checking->end() )
            iterLicense++;
    }

    m_ListOfLicenseThreads.Clear();
}

//
// Creates threads to check the licenses
//
void TNodBase::CheckValidityOfLicenses(PassServer *server, LinkListLicense *validLicenses,License *excludedLicense , int maxValidLicenses )
{
    if ( server && validLicenses )
        CreateLicenseThreads(&server->licenseList,LicenseChecking::CHECK_VALIDITY,validLicenses,excludedLicense,maxValidLicenses);
}


class tkTextInputStream :public wxTextInputStream
{
public:
    tkTextInputStream(wxInputStream& s,
                  const wxString &sep=wxT(" \t"),
                  const wxMBConv& conv = wxConvAuto()):wxTextInputStream(s,sep,conv)
    {

    }

    wxChar NextChar()
    {
        wxChar wbuf[2];
        memset((void*)m_lastBytes, 0, 10);
        for(size_t inlen = 0; inlen < 9; inlen++)
        {
            // actually read the next character
            m_lastBytes[inlen] = m_input.GetC();
            
            if (m_lastBytes[inlen] <= 0)
                return wxEOT;

            if(m_input.LastRead() <= 0)
                return wxEOT;

            if ( m_conv->ToWChar(wbuf, WXSIZEOF(wbuf), m_lastBytes, inlen + 1)
                    != wxCONV_FAILED )
                return wbuf[0];
        }
        // there should be no encoding which requires more than nine bytes for one character...
        return wxEOT;
    }

    wxString ReadLine()
    {
        wxString line;

        while ( !m_input.Eof() )
        {
            wxChar c = NextChar();
            if(c == wxEOT)
                break;

            if (EatEOL(c))
                break;

            line += c;
            if (line.Len() >= 2000 && c == wxT(' '))
                break;
        }

        return line;
    }
};
void TNodBase::FindLicenses(LinkListLicense *validLicenses,License *excludedLicense, int* totalValidServers)
{
    /*
    Steps to get a valid license:
    1. Get a server from the server list.
    2. Download licenses from selected server.
    3. Test all downloaded licenses in the Eset server, if all licenses are invalid return to step 3
    4. End
    */
    if ( (m_Actions2Do & GET_LICENSE) != GET_LICENSE )
        return;

    bool isServerOk = false;
    int responseCode = 0;
    wxInputStream *httpStream = NULL;    
    wxHTTPBuilder *get = NULL;

    wxString lineOfData;  //one line of information from socket

    if ( totalValidServers != NULL )
        *totalValidServers = 0;

    //
    //-------------- Download licenses
    //  
    PassServers servers;
    servers.Load();         //load all pass servers
    LinkListServer::iterator iter;
    PassServer *server = NULL;
    wxZlibInputStream *zipStream = NULL;
    tkTextInputStream *textStream = NULL;
    wxInputStream *stream2Check = NULL;

    for (iter = servers.m_serverList.begin(); 
        iter != servers.m_serverList.end() && validLicenses->GetCount() < wxGetApp().m_validLicenses2Get && !TestDestroy(); 
        ++iter)
    {   
        server = *iter;
        server->clearLicenseList();//clear the list of licenses in the current server

        isServerOk = false;
        get = NULL;

        if ( server != NULL )
        {            
            wxGetApp().ChangeMsgBalloon(1,GETLANG(65) /*+ wxT("\n") + server->GetUrl() */, true);         //downloading licenses.message
            get  = Utils::SockInit( SETTINGS->GetProxySettings() );
            wxURI uriServer(server->GetUrl());

            get->SetHeader(wxT("Accept-Encoding"),wxT("gzip, x-gzip, identity, *;q=0"));
            get->SetHeader(wxT("Referer"), uriServer.GetScheme() + wxT("://") + uriServer.GetServer() );
            httpStream = get->GetInputStream(server->GetUrl());                                   //gets the stream manager        
            responseCode = get->GetResponse();                                        //gets the response code

            //wxMessageBox(get->GetHeader(wxT("Content-Encoding")));           
            //wxMessageBox(get->GetHeader(wxT("Content-Length")));

            if ( responseCode >= 200 && responseCode < 300 && get->GetError() == wxPROTO_NOERR )
                isServerOk = true;

            if ( httpStream == NULL )
                isServerOk = false;
        }

        if ( isServerOk == true )
        {
            if ( totalValidServers != NULL )
                (*totalValidServers)++;
            
            if ( get->GetHeader(wxT("Content-Encoding")).Contains(wxT("gzip")) )
            {//site compressed with gzip
                zipStream = new wxZlibInputStream(*httpStream);
                textStream = new tkTextInputStream(*zipStream);
                stream2Check = zipStream;
            }
            else
            {//normal
                textStream = new tkTextInputStream(*httpStream);
                stream2Check = httpStream;
            }

            //wxTextInputStream in(*httpStream);
            //::wxStartTimer();
            while (!stream2Check->Eof() && !TestDestroy())
            {
                lineOfData = textStream->ReadLine();
                lineOfData += wxT(" ") + textStream->ReadLine();
                lineOfData += wxT(" ") + textStream->ReadLine();

                //wxMessageBox(lineOfData);

                bool stopReading = servers.processInfo(server, lineOfData, wxGetApp().m_licensesNum2Check);
                if ( stopReading == true || httpStream->LastRead() == 0 )
                   break;
            }
            /*wxString msg;
            msg.Printf(wxT("%ld seconds!"),::wxGetElapsedTime(true));
            wxGetApp().ChangeMsgBalloon(1,msg,true);
            tkSleep(3000);*/
        }
        
        if ( get != NULL )
        {
            get->Stop();
            get->Close();
        }
        wxDELETE(httpStream)
        wxDELETE(textStream)
        wxDELETE(zipStream)

        //test the licenses
        CheckLicensesList(server, validLicenses, excludedLicense);

        //clear list of licenses in the current server
        if ( server != NULL )
            server->clearLicenseList();
    }    

    if ( get != NULL )
    {
        get->Stop();
        get->Close();
    }
    wxDELETE(httpStream)

    servers.clearServerList();
}

void TNodBase::CheckLicensesList(PassServer *server, LinkListLicense *validLicenses, License * excludedLicense)
{
    CheckValidityOfLicenses(server,validLicenses,excludedLicense,wxGetApp().m_validLicenses2Get);
}

// --------------------------------------------------
// Returns true if the username and password is valid
// --------------------------------------------------
bool TNodBase::IsValidLicense(License &license)
{
    license.SetValid(false);

    LicenseChecking *thread = new LicenseChecking(&license,NULL, wxTHREAD_JOINABLE);
    if ( thread->Create() == wxTHREAD_NO_ERROR )
    {
        thread->Run();
        thread->Wait();
    }
    delete thread;
    return license.IsValid();
}

// ------------------------------------------------------------------------------------------------------------------------
// Initializations
// ------------------------------------------------------------------------------------------------------------------------
void *TNodBase::Entry()
{
    if ( !Utils::IsConnectionAvailable( SETTINGS->GetProxySettings() ) )
    {
        wxGetApp().ChangeMsgBalloon(3,GETLANG(147));//no internet connection. message
        return NULL;
    }

    if ( m_Actions2Do == 0 )
        m_Actions2Do = ALL_ACTIONS;

    Start();
    return NULL;
}
// ------------------------------------------------------------------------------------------------------------------------
// Commands to be executed when the thread ends
// ------------------------------------------------------------------------------------------------------------------------
void TNodBase::OnExit()
{
    wxGetApp().m_NewVersionFound = false;

    m_CheckedLicenses.DeleteContents(true);
    m_ValidLicenses.Clear();

    if ( m_DownloadEvent != NULL && m_HandlerForEvent != NULL )
    {
        //end event, for downloader dialog
        wxCommandEvent newEvent(*m_DownloadEvent);
        m_DownloadEvent = NULL;
        m_HandlerForEvent = NULL;

        newEvent.SetInt(2);
        wxPostEvent( m_HandlerForEvent, newEvent );
    }

    wxDELETE( m_EsetInfo )
    wxDELETE( m_NewVersionMsg )

    if ( m_endIndicator )
        *m_endIndicator = NULL;

    if ( m_IsMain )
    {
        //if this thread is the main, check until the context Menu is closed
        while ( wxGetApp().m_MenuShown != NULL && !TestDestroy() )
        {
            this->Sleep(1000);
        }
        wxGetApp().Finish();//end app
    }
}

// ------------------------------------------------------------------------------------------------------------------------
// Shows the -new version- message
// ------------------------------------------------------------------------------------------------------------------------
void TNodBase::ProcessNewVersionMsg(NewVersionMsg *newVersionMsg)
{
    if (TestDestroy() || m_ProcessNewVersionMsg == false )
        return;

    if ( (m_Actions2Do & CHECK_UPDATES) != CHECK_UPDATES || newVersionMsg == NULL )
        return;

    if ( m_MsgWithNewVersionOnly == true && newVersionMsg->m_isNewVersionAvailable == false )
        return;

    UpdatesChecker::ProcessNewVersionMsg( newVersionMsg );
}
void TNodBase::SetEndIndicator(void **indicator)
{
    m_endIndicator = indicator;
}
void TNodBase::SetAsMain()
{
    m_IsMain = true;
}
void TNodBase::SetProcessNewVersionMsg(bool process, bool msgWithNewVersionOnly)
{
    m_ProcessNewVersionMsg = process;
    m_MsgWithNewVersionOnly = msgWithNewVersionOnly;
}